#include "log.h"

Log::Log() {
    m_lineCount = 0;
    m_isAsync = false;
    m_writeThread = nullptr;
    m_deque = nullptr;
    m_today = 0;
    m_fp = nullptr;
}

Log::~Log() {
    if (m_writeThread && m_writeThread->joinable()) {
        while(!m_deque->Empty()) {
            m_deque->Flush();
        }
        m_deque->Close();
        m_writeThread->join();
    }
    if (m_fp) {
        std::lock_guard<std::mutex> locker(mtx);
        Flush();
        fclose(m_fp);
    }
}

Log* Log::Instance() {
    static Log instance; // 局部静态变量，线程安全
    return &instance;
}

int Log::GetLevel() {
    std::lock_guard<std::mutex> locker(mtx);
    return m_level;
}



void Log::Flush() {
    if (m_isAsync) {
        m_deque->Flush();
    }
    fflush(m_fp);
}

void Log::Init(int level = 1, const char* path, const char* suffix, 
    int maxdequesize) {
        m_isOpen = true;
        m_level = level;
        if (maxdequesize > 0) {
            m_isAsync = true;
            if (!m_deque) {
                std::unique_ptr<BlockDeque<std::string>> newdeque(new BlockDeque<std::string>);
                m_deque = std::move(newdeque);
                // 没有join或detach，析构时系统会调用std::terminate中断程序
                std::unique_ptr<std::thread> newthread(new std::thread(FlushLogThread));
                m_writeThread = std::move(newthread);
            }
        } else {
            m_isAsync = false;
        }

        m_lineCount = 0;

        time_t timer = time(nullptr);
        struct tm *systime = localtime(&timer);
        struct tm t = *systime;

        m_path = path;
        m_suffix = suffix;
        
        char filename[LOG_NAME_LEN] = {0};
        snprintf(filename, LOG_NAME_LEN - 1, "%s/%04d_%02d_%02d%s", 
            m_path, t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, m_suffix);
        m_today = t.tm_mday;

        {
            std::lock_guard<std::mutex> locker(mtx);
            // 此处有buffer未写
            if (m_fp) {
                Flush();
                fclose(m_fp);
            }

            m_fp = fopen(filename, "a");

            if (m_fp == nullptr) {
                mkdir(m_path, 0777);
                m_fp = fopen(filename, "a");
            }
            assert(m_fp != nullptr);
        }
    }

void Log::Write(int level, const char* format, ...) {
    struct timeval now = {0, 0};
    gettimeofday(&now, nullptr);
    time_t tsec = now.tv_sec;
    struct tm *systime = localtime(&tsec);
    struct tm t = *systime;
    va_list valist;
    // 今天并不是类中保存的日期（服务器一直运行过了24点）或者日志行数到达最大行，就新建日志文件
    if (m_today != t.tm_mday || (m_lineCount && (m_lineCount % MAX_LINES == 0))) {
        std::unique_lock<std::mutex> locker(mtx);
        locker.unlock();

        char newFile[LOG_NAME_LEN];
        char tail[36] = {0}; // 文件名
        snprintf(tail, 36, "%04d_%02d_%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday); // 格式化字符串放入tail中

        if (m_today != t.tm_mday) { // 更改日期
            snprintf(newFile, LOG_NAME_LEN - 72, "%s/%s%s", m_path, tail, m_suffix);
            m_today = t.tm_mday;
            m_lineCount = 0;
        } else { // 文件行数过大
            snprintf(newFile, LOG_NAME_LEN - 72, "%s/%s-%d%s", m_path, tail, (m_lineCount / MAX_LINES), m_suffix);
        }
        locker.lock();
        Flush();
        fclose(m_fp);
        m_fp = fopen(newFile, "a"); // 写入数据加入文件尾
        assert(m_fp != nullptr);
    }
    // 写数据
    {
        std::unique_lock<std::mutex> locker(mtx);
        m_lineCount++;

        // buff暂时不写,这一段还要修改
        char content[256] = {0};
        // 获得可变参数valist，并给content赋值
        va_start(valist, format);
        vsprintf(content, format, valist);
        va_end(valist);

        std::string s(content);
        s = s + "\n\0";
        // 同步则直接fputs，异步加入阻塞队列
        if (m_isAsync && m_deque && !m_deque->Full()) {
            m_deque->PushBack(s);
        } else {
            fputs(s.c_str(), m_fp);
        }
    }
}

void Log::AsyncWrite() {
    std::string str = "";
    while(m_deque->Pop(str)) {
        std::lock_guard<std::mutex> locker(mtx);
        fputs(str.c_str(), m_fp);
    }
}

void Log::FlushLogThread() {
    Log::Instance()->AsyncWrite();
}